#include "read_and_write_flash.h"
#include "cJSON.h"
#include "bsp.h"
#include "atiny_adapter.h"
#include "app_common.h"
#include "mcu_log.h"

void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead);
void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
void W25QXX_Erase_Sector(uint32_t Dst_Addr);
int get_sha256(unsigned char *pData,unsigned long iLen,char *pSha256);



#define LOGRECORD_ZONE_START_ADDR      FILE_MCU_LOG_ADDR
#define LOGRECORD_ZONE_SIZE            FILE_MCU_LOG_SIZE  //32k

static zone_info zone_info_table[] = {
    { .id            = LOGRECORD_ZONE,
      .start_address = FILE_MCU_LOG_ADDR,
      .end_address   = FILE_MCU_LOG_ADDR + FILE_MCU_LOG_SIZE - 1
    },
};

static flash_zone_t g_flash_data_zone[] = {
	{TEMP_R_MAP_ZONE, 2, 0x1000, 0x2000, 0x3000},
	{LOG_RECORD_ZONE, FILE_MCU_LOG_SECTOR_NO, FILE_MCU_LOG_SIZE, FILE_MCU_LOG_ADDR, FILE_MCU_LOG_ADDR+FILE_MCU_LOG_SIZE-1},
};

flash_zone_t *get_flash_zone(flash_zone_id id)
{
    int i = 0;
    int zone_count = sizeof(g_flash_data_zone) / sizeof(zone_info);
    for (i = 0; i < zone_count; i++)
    {
        if (id == g_flash_data_zone[i].id) {
            return &g_flash_data_zone[i];
        }
    }
    return NULL;
}


/*
 * 功能: 提供flash读功能
 * 返回: 0:成功 其他：失败
 * 注意: 1) hal_flash_read为参考示例接口，最终由用户重新替换该接口
 *       2) 此函数由开发者实现
 */
int mcu_flash_read(void* buf, int32_t len, uint32_t location)
{
	memset(buf ,0, len);
    if(len < FLASH_READ_AND_WRITE_MAX_LEN){
        W25QXX_Read(buf, location, len);
    }
	else{
        int read_len = 0;
        while(len - read_len > FLASH_READ_AND_WRITE_MAX_LEN){
            W25QXX_Read(buf+read_len, location + read_len, FLASH_READ_AND_WRITE_MAX_LEN);
            read_len = read_len + FLASH_READ_AND_WRITE_MAX_LEN;
        }
        W25QXX_Read(buf+read_len, location+read_len, len-read_len);
    }
	
    return 0;
}


/*
 * 功能: 提供flash写功能
 * 返回: 0:成功 其他：失败
 * 注意: 1) hal_flash_write为参考示例接口，最终由用户重新替换该接口
 *       2) 此函数由开发者实现
 */
int mcu_flash_write(void* buf, int32_t len, uint32_t location)
{
	uint8_t *pbuf = (uint8_t *)buf;
	if(len <= FLASH_READ_AND_WRITE_MAX_LEN){
		W25QXX_Write(pbuf, location, len); //从地址0开始写
	}
	else {
		int write_len = 0;
		while(len - write_len > FLASH_READ_AND_WRITE_MAX_LEN){
			W25QXX_Write(pbuf+write_len, location+write_len, FLASH_READ_AND_WRITE_MAX_LEN);
			write_len += FLASH_READ_AND_WRITE_MAX_LEN;
		}
		W25QXX_Write(pbuf+write_len, location+write_len, len-write_len);
	}
    return 0;
}

int flash_read_zone(flash_zone_id id, void *data, uint16_t len)
{
    flash_zone_t *flash_info = get_flash_zone(id);
    if (flash_info == NULL) {
        OMIP_LOG_INFO("[%s] get flash zone (%d) failed\n", __FUNCTION__, id);
        return -1;
    }
	if(len > flash_info->zone_size) {
        OMIP_LOG_INFO("[%s] length (%d) is invalid\n", __FUNCTION__, len);
        return -1;
	}
	memset(data ,0, len);
	uint16_t read_len = 0;
	uint16_t sum_len = 0;
	while(!(sum_len == len))
	{
		if(len-read_len > FLASH_RW_MAX_LEN) {
			read_len = FLASH_RW_MAX_LEN;
		}
		else {
			read_len = len - sum_len;
		}
		W25QXX_Read(data+sum_len, flash_info->start_address+sum_len, read_len);
		sum_len += read_len;
	}
	return 0;
}

int flash_write_zone(flash_zone_id id, void *data, uint16_t len)
{
    flash_zone_t *flash_info = get_flash_zone(id);
    if (flash_info == NULL) {
        OMIP_LOG_INFO("[%s] get flash zone (%d) failed\n", __FUNCTION__, id);
        return -1;
    }
	if(len > flash_info->zone_size) {
        OMIP_LOG_INFO("[%s] length (%d) is invalid\n", __FUNCTION__, len);
        return -1;
	}
	uint16_t sum_len = 0;
	uint16_t write_len = 0;
	while(!(sum_len == len))
	{
		if(len-write_len > FLASH_RW_MAX_LEN) {
			write_len = FLASH_RW_MAX_LEN;
		}
		else {
			write_len = len - sum_len;
		}
		W25QXX_Write(data+sum_len, flash_info->start_address+sum_len, write_len);
		sum_len += write_len;
	}
	return 0;
}


int flash_rw_zone_test()
{
	flash_zone_t *flash_info = get_flash_zone(TEMP_R_MAP_ZONE);
    if (flash_info == NULL) {
        OMIP_LOG_INFO("[%s] get flash zone (%d) failed\n", __FUNCTION__, TEMP_R_MAP_ZONE);
        return -1;
    }
	uint8_t * buf = malloc(flash_info->zone_size);
	if(NULL == buf) {
		print_debug(0, "malloc memrey failed, size:%ld", flash_info->zone_size);
		return -1;
	}
	char w_sha256[65] ={0};
	char r_sha256[65] ={0};
	get_sha256(buf, flash_info->zone_size, w_sha256);
	print_debug(0, "w_sha256:%s", w_sha256);
	flash_write_zone(TEMP_R_MAP_ZONE, buf, flash_info->zone_size);
	memset(buf, 0, flash_info->zone_size);
	flash_read_zone(TEMP_R_MAP_ZONE, buf, flash_info->zone_size);
	get_sha256(buf, flash_info->zone_size, r_sha256);
	print_debug(0, "w_sha256:%s", r_sha256);
	if(strcasecmp(w_sha256, r_sha256) != 0)
	{
		print_debug(0, "flash read write zone test failed");
		return -1;
	}
	
	print_debug(0, "flash read write zone test sucessful");
	return 0;
}



zone_info *mcu_get_flash_zone(zone_id id)
{
    int i = 0;
    int zone_count = sizeof(zone_info_table) / sizeof(zone_info);
    for (i = 0; i < zone_count; i++)
    {
        if (id == zone_info_table[i].id)
        {
            return (zone_info *) &zone_info_table[i];
        }
    }
    return NULL;
}

int flash_erase_zone(zone_id id)
{
    zone_info *zone = mcu_get_flash_zone(id);
    if (zone == NULL) {
        OMIP_LOG_INFO("[%s] get flash zone (%d) failed\n", __FUNCTION__, id);
        return -1;
    }
	uint32_t secpos_id = zone->start_address/FLASH_SECTOR_SIZE;
	while(secpos_id*FLASH_SECTOR_SIZE < zone->end_address)
	{
		W25QXX_Erase_Sector(secpos_id);
		secpos_id++;
	}
	return 0;
}


void mcu_flash_log_zone_init()
{
	zone_info *zone = mcu_get_flash_zone(LOGRECORD_ZONE);
	if(zone == NULL) {
		printf("[%s:%d]\r\n", __FUNCTION__, __LINE__);
		return;
	}
	uint8_t * buf = malloc(FLASH_RW_MAX_LEN);
	if(buf == NULL) {
		//MCU_LOG(MCU_LOG_INFO_UPLOAD, "flash zone init, malloc failed\r\n");
		printf("flash zone init, malloc failed\r\n");
		return;
	}
	memset(buf, 0, FLASH_RW_MAX_LEN);
	zone->last_address = zone->start_address;
		
	#if 1
	while(1)
	{
		printf("start_address:0x%x, last_address:0x%x\r\n", zone->start_address, zone->last_address);
		mcu_flash_read(buf, FLASH_RW_MAX_LEN, zone->last_address);
		for(int i = 0; i < FLASH_RW_MAX_LEN; i++)
		{
			if(zone->last_address > zone->end_address) {
				//MCU_LOG(MCU_LOG_INFO_UPLOAD, "flash zone init, zone full restart\r\n");
				printf("flash zone init, zone full restart\r\n");
				zone->last_address = zone->start_address;
				free(buf);
				return;
			}
			
			if(buf[i] == 0xFF) {
				//MCU_LOG(MCU_LOG_INFO_UPLOAD, "flash zone init, last_address=%d\r\n", zone->last_address);
				printf("flash zone init, last_address=0x%x\r\n", zone->last_address);
				free(buf);
				return;
			}
			zone->last_address++;
		}
	}
	#endif
	zone->last_address = zone->start_address;
	free(buf);
	return;
}

int mcu_erase_sector_flash(zone_id id, unsigned int address, int length)
{
    zone_info *zone_info_tmp = mcu_get_flash_zone(id);
    if (zone_info_tmp == NULL)
    {
        printf("[%s] get zone (%d) failed\n", __FUNCTION__, id);
        return -1;
    }
    if(length <= 0)
    {
        printf("[%s] length (%d) is invalid\n", __FUNCTION__, length);
        return -1;
    }
    if ((address < zone_info_tmp->start_address) || ((address + length - 1) > zone_info_tmp->end_address))
    {
        printf("[%s] length (%d) is out of range\n", __FUNCTION__, length);
        return -1;
    }
	if(address%OMIP_FLASH_SECTOR_SIZE != 0) {
        printf("[%s] address (%d) is invalid\n", __FUNCTION__, address);
        return -1;
	}
	uint32_t secpos = address/OMIP_FLASH_SECTOR_SIZE;
	W25QXX_Erase_Sector(secpos);//擦除这个扇区
    return 0;
}

int mcu_erase_flash(zone_id id, int length)
{
    zone_info *zone_info_tmp = mcu_get_flash_zone(id);
    if (zone_info_tmp == NULL)
    {
        printf("[%s] get zone (%d) failed\n", __FUNCTION__, id);
        return -1;
    }

    if(length <= 0)
    {
        printf("[%s] length (%d) is invalid\n", __FUNCTION__, length);
        return -1;
    }

    if ((zone_info_tmp->start_address + length - 1) > zone_info_tmp->end_address)
    {
        printf("[%s] length (%d) is out of range\n", __FUNCTION__, length);
        return -1;
    }

    uint32_t sector_addr = zone_info_tmp->start_address;
    uint32_t sector_num = length / OMIP_FLASH_SECTOR_SIZE + ((length % OMIP_FLASH_SECTOR_SIZE) == 0 ? 0 : 1);
    uint32_t i = 0;
	uint32_t secpos = sector_addr/OMIP_FLASH_SECTOR_SIZE;

    for (i = 0; i < sector_num; i++) {
        W25QXX_Erase_Sector(secpos + i);
    }
    return 0;
}


int mcu_read_flash(zone_id id, unsigned int address, uint8_t* buffer, unsigned int length)
{
    zone_info *zone_info_tmp = mcu_get_flash_zone(id);

    if (zone_info_tmp == NULL)
    {
        printf("[%s] get zone (%d) failed\n", __FUNCTION__, id);
        return -1;
    }

    if ((address < zone_info_tmp->start_address) || ((address + length - 1) > zone_info_tmp->end_address))
    {
        printf("[%s] length (%d) is out of range\n", __FUNCTION__, length);
        return -1;
    }

    if ((buffer == NULL) || (length <= 0))
    {
        printf("[%s] buffer or length (%d) is invalid\n", __FUNCTION__, length);
        return -1;
    }

    memset(buffer, 0, length);
    if (mcu_flash_read(buffer, length, address) != 0)
    {
        printf("[%s] read flash (0x%8x) length (%d) failed\n", __FUNCTION__, address, length);
        return -1;
    }

    return 0;
}

int mcu_write_flash(zone_id id, unsigned int address, const uint8_t* buffer, unsigned int length)
{
    zone_info *zone_info_tmp = mcu_get_flash_zone(id);

    if (zone_info_tmp == NULL)
    {
         printf("[%s] get zone (%d) failed\n", __FUNCTION__, id);
         return -1;
    }

    if ((buffer == NULL) || (length <= 0))
    {
        printf("[%s] buffer or length (%d) is invalid\n", __FUNCTION__, length);
        return -1;
    }

    if ((address < zone_info_tmp->start_address) || ((address + length - 1) > zone_info_tmp->end_address))
    {
        printf("[%s] length (%d) is out of range\n", __FUNCTION__, length);
        return -1;
    }

    uint32_t w_address = address;
    if (mcu_flash_write((void *)buffer, length, w_address) != 0)
    {
        printf("[%s] write flash (0x%8lx) length (%d) failed\n", __FUNCTION__, w_address, length);
        return -1;
    }

    return 0;
}


void free_w25flash_t(w25flash_t **pData)
{
	if(NULL ==(*pData)) return;
    if(NULL !=(*pData)->buff)	free((*pData)->buff);
    if(NULL !=(*pData))			free((*pData));
	*pData =NULL;
}

w25flash_t *file_addr_init(char *filename, uint32_t start_addr, uint32_t size){
    w25flash_t *temp = NULL;
    temp = (w25flash_t *)malloc(sizeof(w25flash_t));
    if(NULL ==temp) {omip_printf(OMIP_INFO,"file_addr_init() malloc failed!!\r\n"); return NULL;}
    memset(temp,0,sizeof(w25flash_t));
    strcpy((char *)temp->file_name,filename); //filename len
    temp->size = size;
    temp->flash_start_addr = start_addr;
    temp->buff = NULL;
    return temp;
}

void read_data_from_file(w25flash_t *file)
{
    if(file->size < FLASH_READ_AND_WRITE_MAX_LEN){
        memset(file->buff ,0,sizeof(uint8_t) *file->size);
        // omip_printf(OMIP_INFO,"this is file_name:%s %d\r\n",file->file_name,file->size);
        W25QXX_Read((uint8_t *)file->buff,file->flash_start_addr,file->size);
        // omip_printf(OMIP_INFO,"%s \r\n",file->buff);
    }
	else{
        int i = 0;
        unsigned char *temp;
        temp = malloc(FLASH_READ_AND_WRITE_MAX_LEN);
        while(file->size - i*FLASH_READ_AND_WRITE_MAX_LEN >FLASH_READ_AND_WRITE_MAX_LEN){
            W25QXX_Read(temp,file->flash_start_addr + i*FLASH_READ_AND_WRITE_MAX_LEN,FLASH_READ_AND_WRITE_MAX_LEN);
            memcpy(file->buff + i*FLASH_READ_AND_WRITE_MAX_LEN, temp,FLASH_READ_AND_WRITE_MAX_LEN);
            i = i + 1;
        }
        free(temp);
        W25QXX_Read(temp,file->flash_start_addr + i*FLASH_READ_AND_WRITE_MAX_LEN,file->size - i*FLASH_READ_AND_WRITE_MAX_LEN);
        memcpy(file->buff + i*FLASH_READ_AND_WRITE_MAX_LEN, temp, file->size - i*FLASH_READ_AND_WRITE_MAX_LEN);
    }
    // omip_printf(OMIP_INFO,"this is read_data:%s \r\n",file->buff);
}

int save_data_to_file(w25flash_t *file)
{
    if(file->buff == NULL){
        OMIP_LOG_ERROR("%s is null, can not save",file->file_name);
		return -1;
    }
	else{
        if(file->size <= FLASH_READ_AND_WRITE_MAX_LEN){
            // omip_printf(OMIP_INFO,"this is save data:%s,len:%d %s %d \r\n",file->buff,strlen(file->buff),__FUNCTION__,__LINE__);
            W25QXX_Write((uint8_t *)file->buff,file->flash_start_addr,file->size);//从地址0开始写
        }else
        {
            int i = 0;
            unsigned char *temp;
            temp = malloc(FLASH_READ_AND_WRITE_MAX_LEN);
            while(file->size - i*FLASH_READ_AND_WRITE_MAX_LEN >FLASH_READ_AND_WRITE_MAX_LEN){
                memcpy(temp, file->buff +i*FLASH_READ_AND_WRITE_MAX_LEN,FLASH_READ_AND_WRITE_MAX_LEN);
                W25QXX_Write(temp,file->flash_start_addr + i*FLASH_READ_AND_WRITE_MAX_LEN,FLASH_READ_AND_WRITE_MAX_LEN);//从地址0开始写
                i = i + 1;
            }
            free(temp);
            W25QXX_Write((uint8_t *)file->buff + i*FLASH_READ_AND_WRITE_MAX_LEN ,file->flash_start_addr + i*FLASH_READ_AND_WRITE_MAX_LEN ,file->size - i*FLASH_READ_AND_WRITE_MAX_LEN);//从地址0开始写
        }
        omip_printf(OMIP_INFO,"file %s finished write \r\n",file->file_name);
        int flag = 0x77;
        int length = file->size;
        if(strcmp((char *)file->file_name,"flow") == 0) {
            W25QXX_Write((uint8_t *)&flag, FILE_FLOW_FLAG_ADDR, FILE_FLAG_LENGTH);
            W25QXX_Write((uint8_t *)&length, FILE_FLOW_LENGTH_ADDR, FILE_LENGTH_LENGTH);
            printf("flow length :%d \r\n", length);
        } else if(strcmp((char *)file->file_name,"bin") == 0) {
            W25QXX_Write((uint8_t *)&flag, FILE_BIN_FLAG_ADDR, FILE_FLAG_LENGTH);
            W25QXX_Write((uint8_t *)&length, FILE_BIN_LENGTH_ADDR, FILE_LENGTH_LENGTH);
            W25QXX_Write((uint8_t *)&flag, FILE_BIN_UPDATE_FLAG_ADDR, FILE_FLAG_LENGTH);
        }
    }
    return 0;
}

#ifdef USE_SRAM_BCKUP
sram_t *file_addr_init_ext(uint8_t *filename, uint32_t start_addr, uint32_t size){
    sram_t *temp = NULL;
    temp = (sram_t *)malloc(sizeof(sram_t));
    if(NULL == temp){
        omip_printf(OMIP_INFO,"FILE INIT FAILED");
        return NULL;
    }
    memset(temp,0,sizeof(sram_t));
    //TODO filename len
    strcpy((char *)temp->file_name, (char *)filename);
    temp->size = size;
    temp->flash_start_addr = start_addr;
    temp->buff = NULL;
    return temp;
}

void read_data_from_file_ext(sram_t *file)
{
    if(file->size < BACKUP_SRAM_SIZE){
        memset(file->buff ,0,sizeof(uint8_t) *file->size);
        // omip_printf(OMIP_INFO,"this is file_name:%s %d\r\n",file->file_name,file->size);
        BackupSRAM_ReadData(file->buff,file->flash_start_addr,file->size);
        // omip_printf(OMIP_INFO,"%s \r\n",file->buff);
    }
	else{
        int i = 0;
        unsigned char *temp;
        temp = malloc(BACKUP_SRAM_SIZE);
        while(file->size - i*BACKUP_SRAM_SIZE >BACKUP_SRAM_SIZE){
            BackupSRAM_ReadData(temp,file->flash_start_addr + i*BACKUP_SRAM_SIZE,BACKUP_SRAM_SIZE);
            memcpy(file->buff + i*BACKUP_SRAM_SIZE, temp,BACKUP_SRAM_SIZE);
            i = i + 1;
        }
        free(temp);
        BackupSRAM_ReadData(temp,file->flash_start_addr + i*BACKUP_SRAM_SIZE,file->size - i*BACKUP_SRAM_SIZE);
        memcpy(file->buff + i*BACKUP_SRAM_SIZE, temp, file->size - i*BACKUP_SRAM_SIZE);
    }
    // omip_printf(OMIP_INFO,"this is read_data:%s \r\n",file->buff);
}

int save_data_to_file_ext(sram_t *file)
{
    if(file->buff == NULL){
        omip_printf(OMIP_INFO,"%s is null, can not save",file->file_name);
    }
	else{
        if(file->size <= BACKUP_SRAM_SIZE){
            // omip_printf(OMIP_INFO,"this is save data:%s,len:%d %s %d \r\n",file->buff,strlen(file->buff),__FUNCTION__,__LINE__);
            BackupSRAM_WriteData(file->buff,file->flash_start_addr,file->size);//从地址0开始写
        }
		else
        {
            int i = 0;
            unsigned char *temp;
            temp = malloc(BACKUP_SRAM_SIZE);
            while(file->size - i*BACKUP_SRAM_SIZE >BACKUP_SRAM_SIZE){
                memcpy(temp, file->buff +i*BACKUP_SRAM_SIZE,BACKUP_SRAM_SIZE);
                BackupSRAM_WriteData(temp,file->flash_start_addr + i*BACKUP_SRAM_SIZE,BACKUP_SRAM_SIZE);//从地址0开始写
                i = i + 1;
            }
            free(temp);
            BackupSRAM_WriteData(file->buff + i*BACKUP_SRAM_SIZE ,file->flash_start_addr + i*BACKUP_SRAM_SIZE ,file->size - i*BACKUP_SRAM_SIZE);//从地址0开始写
        }
        omip_printf(OMIP_INFO,"file %s finished write \r\n",file->file_name);
    }
    return 0;
}
#endif
